#include <lib/list.h>
#include <os/waitqueue.h>
#include <os/task.h>
#include <os/schedule.h>
#include <os/spinlock.h>
#include<assert.h>

void WaitQueueAdd(wait_queue_t *wait_queue, void *task)
{
    SpinLockDisInterrupt(&wait_queue->lock);
    if (!list_find(&((task_t *)task)->list, &wait_queue->wait_list))
    {
        list_add_tail(&((task_t *)task)->list, &wait_queue->wait_list);
        TASK_INTO_WAITLIST((task_t *)task);
    }
    SpinUnlockEnInterrupt(&wait_queue->lock);      
}

void WaitQueueExit(wait_queue_t *wait_queue, void *task)
{
    SpinLockDisInterrupt(&wait_queue->lock);

    if (list_find(&((task_t *)task)->list, &wait_queue->wait_list))
    {
        list_del_init(&((task_t *)task)->list);
        TASK_EXIT_WAITLIST((task_t *)task);
    }
    SpinUnlockEnInterrupt(&wait_queue->lock);
}

void WaitQueueSleep(wait_queue_t *wait_queue)
{
    task_t *cur = cur_task;

    // add current task to wait queue
    WaitQueueAdd(wait_queue, cur);
    // task block
    TaskBlock(TASK_BLOCKED);
}

void WaitQueueWakeup(wait_queue_t *wait_queue)
{
    task_t *task = NULL;

    SpinLockDisInterrupt(&wait_queue->lock);
    if (!list_empty(&wait_queue->wait_list))
    {
        task = list_first_owner(&wait_queue->wait_list, task_t, list);
        if (task != NULL)
        {
            list_del_init(&task->list);
            TASK_EXIT_WAITLIST(task);
            TaskUnBlock(task);
        }
    }
    SpinUnlockEnInterrupt(&wait_queue->lock);
}

void WaitQueueWakeupAll(wait_queue_t *wait_queue)
{
    task_t *task = NULL;

    SpinLockDisInterrupt(&wait_queue->lock);
    list_traversal_all_owner_to_next(task, &wait_queue->wait_list, list)
    {
        // del task from watting list
        list_del_init(&task->list);
        TASK_EXIT_WAITLIST(task);
        TaskUnBlock(task);
    }
    SpinUnlockEnInterrupt(&wait_queue->lock);
}

uint64_t WaitQueueLen(wait_queue_t *queue)
{
    task_t *task;
    uint64_t count=0;

    SpinLockDisInterrupt(&queue->lock);
    list_traversal_all_owner_to_next(task, &queue->wait_list, list)
    {
        count++;
    }
    SpinUnlockEnInterrupt(&queue->lock);
    return count;
}